#pragma once

#include "ConvexHull.h"

enum ConvexHullMergerState
{
	SearchingUpperTangentLeft,
	SearchingUpperTangentRight,
	SearchingLowerTangentLeft,
	SearchingLowerTangentRight,
	AllTangentsFound,
};

class ConvexHullMerger
{
public:
	inline ConvexHullMerger(const ConvexHull* hullLeft, const ConvexHull* hullRight);
	inline ConvexHullLine GetUpperTangent(void) const;
	inline ConvexHullLine GetLowerTangent(void) const;
	inline void Step(void);
	inline ConvexHull* GetMergedHull(void);
	inline bool isFinished(void) const;
private:
	inline void m_Finish();

	const ConvexHull* m_hullLeft;
	const ConvexHull* m_hullRight;
	ConvexHull* m_hullMerged;
	ConvexHullLine m_tangentUpper;
	ConvexHullLine m_tangentLower;
	ConvexHullMergerState m_state;
};

inline ConvexHullMerger::ConvexHullMerger(const ConvexHull* hullLeft, const ConvexHull* hullRight)
	: m_hullLeft(hullLeft),
	m_hullRight(hullRight),
	m_tangentLower(hullLeft->rightMostPoint, hullRight->leftMostPoint),
	m_tangentUpper(hullLeft->rightMostPoint, hullRight->leftMostPoint),
	m_state(ConvexHullMergerState::SearchingUpperTangentLeft),
	m_hullMerged(nullptr)
{
	
}

ConvexHullLine ConvexHullMerger::GetUpperTangent( void ) const
{
	return m_tangentUpper;
}

ConvexHullLine ConvexHullMerger::GetLowerTangent( void ) const
{
	return m_tangentLower;
}

void ConvexHullMerger::Step( void )
{
	switch (m_state)
	{
	case SearchingUpperTangentLeft:
		while(!m_hullLeft->IsFullyBelowLine(m_tangentUpper))
		{
			m_tangentUpper.left = m_tangentUpper.left->nextPointCounterClockwise;
		}
		if(m_hullRight->IsFullyBelowLine(m_tangentUpper))
		{
			m_state = SearchingLowerTangentLeft;
		}
		else
		{
			m_state = SearchingUpperTangentRight;
		}
		break;
	case SearchingUpperTangentRight:
		while(!m_hullRight->IsFullyBelowLine(m_tangentUpper))
		{
			m_tangentUpper.right = m_tangentUpper.right->nextPointClockwise;
		}
		if(m_hullLeft->IsFullyBelowLine(m_tangentUpper))
		{
			m_state = SearchingLowerTangentLeft;
		}
		else
		{
			m_state = SearchingUpperTangentLeft;
		}
		break;
	case SearchingLowerTangentLeft:
		while(!m_hullLeft->IsFullyAboveLine(m_tangentLower))
		{
			m_tangentLower.left = m_tangentLower.left->nextPointClockwise;
		}
		if(m_hullRight->IsFullyAboveLine(m_tangentLower))
		{
			m_Finish();
		}
		else
		{
			m_state = SearchingLowerTangentRight;
		}
		break;
	case SearchingLowerTangentRight:
		while(!m_hullRight->IsFullyAboveLine(m_tangentLower))
		{
			m_tangentLower.right = m_tangentLower.right->nextPointCounterClockwise;
		}
		if(m_hullLeft->IsFullyAboveLine(m_tangentLower))
		{
			m_Finish();
		}
		else
		{
			m_state = SearchingUpperTangentLeft;
		}
		break;
	case AllTangentsFound:
		assert(0);
	default:
		break;
	}
}

ConvexHull* ConvexHullMerger::GetMergedHull( void )
{
	if(m_state != ConvexHullMergerState::AllTangentsFound)
	{
		return nullptr;
	}else
	{
		return m_hullMerged;
	}
}

inline bool ConvexHullMerger::isFinished( void ) const
{
	return m_state == ConvexHullMergerState::AllTangentsFound;
}

void ConvexHullMerger::m_Finish()
{
	m_tangentUpper.left->DeleteClockWiseUntil(m_tangentLower.left);
	m_tangentLower.right->DeleteClockWiseUntil(m_tangentUpper.right);
	m_tangentUpper.left->nextPointClockwise = m_tangentUpper.right;
	m_tangentUpper.right->nextPointCounterClockwise = m_tangentUpper.left;
	m_tangentLower.left->nextPointCounterClockwise = m_tangentLower.right;
	m_tangentLower.right->nextPointClockwise = m_tangentLower.left;
	m_hullMerged = new ConvexHull(m_hullLeft->leftMostPoint, m_hullRight->rightMostPoint);
	delete m_hullLeft;
	delete m_hullRight;
	m_state = AllTangentsFound;
}


